package com.zjdiepu.www.exception;

import java.net.BindException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.catalina.connector.ClientAbortException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.ConversionNotSupportedException;
import org.springframework.beans.TypeMismatchException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;

import com.alibaba.fastjson.JSON;
import com.qiniu.common.QiniuException;
import com.zjdiepu.www.base.core.BaseException;
import com.zjdiepu.www.base.enums.type.ResultFlagType;
import com.zjdiepu.www.base.lang.HttpClientException;
import com.zjdiepu.www.common.consts.SystemConsts;
import com.zjdiepu.www.config.ServerConfig;
import com.zjdiepu.www.response.BaseResponse;
import com.zjdiepu.www.response.BooleanResponse;
import com.zjdiepu.www.security.exception.BasePartnerException;
import com.zjdiepu.www.security.server.core.Partners;
import com.zjdiepu.www.security.server.encode.ServerEncryptAndDencryptUtil;
import com.zjdiepu.www.security.server.interceptor.PartnerHolder;
import com.zjdiepu.www.security.server.sign.ServerSignFactory;
import com.zjdiepu.www.security.sign.ISign;
import com.zjdiepu.www.security.sign.rsa.RsaSign;
/**
 * 项目名称 dpqc-parent-manager-base <br/> 
 * 类名称 MyHandlerExceptionResolver <br/>  
 * 类描述 全局异常处理 <br/>    
 * 创建人 xiongxiaotun <br/> 
 * 创建时间 2017年4月9日 下午2:08:33  <br/>   
 * 修改人 xiongxiaotun <br/> 
 * 修改时间 2017年4月9日 下午2:08:33   <br/>  
 * 修改备注   <br/> 
 * @version V1.0   <br/>
 */
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
	private static final Logger logger = LoggerFactory.getLogger(MyHandlerExceptionResolver.class);
	
	@Override
	public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
		if (ex == null) {
			return null;
		}
		String requestURI = request.getRequestURI();
		String contextPath = request.getContextPath();
		boolean log = true;
		if(ex instanceof ClientAbortException){ //请求主动断开（比如刷新页面等导致打断请求）
			log = false;
		} else if(ex instanceof BaseException) {
			BaseException bex = (BaseException)ex;
			logger.error("请求发生异常.requestURI:{}.提示信息：{}", requestURI, bex.getErrorMsg());
			log = false;
		} else if(ex instanceof BasePartnerException) {
			BasePartnerException bex = (BasePartnerException)ex;
			logger.error("请求发生异常.requestURI:{}.第三方调用异常,异常编码：{}，异常信息：{}", requestURI, bex.getErrorCode(), bex.getErrorMsg());
			log = false;
		}
		if(log) {
			if(!requestURI.equals(contextPath + "/user/login")){ //登录POST
				logger.error("请求发生异常.requestURI:{}.异常栈:{}", requestURI, ex);
			}
		}
		
		ModelAndView mv = null;
		int statusCode = 500;
		BaseResponse<BooleanResponse> responseFail = null;
		if (ex instanceof NoSuchRequestHandlingMethodException) {
			statusCode = HttpServletResponse.SC_NOT_FOUND;
			responseFail = BaseResponse.failWithBoolean(statusCode, "请求匹配失败");
		} else if (ex instanceof HttpRequestMethodNotSupportedException) {
			responseFail = BaseResponse.failWithBoolean(ResultFlagType.HTTP_REQUEST_METHOD_NOT_SUPPORTED.getCode(), ResultFlagType.HTTP_REQUEST_METHOD_NOT_SUPPORTED.getValue());
		} else if (ex instanceof HttpMediaTypeNotSupportedException) {
			responseFail = BaseResponse.failWithBoolean(ResultFlagType.PARAM_TYPE_MISMATCH.getCode(), ResultFlagType.PARAM_TYPE_MISMATCH.getValue());
		} else if (ex instanceof HttpMediaTypeNotAcceptableException) {
			statusCode = HttpServletResponse.SC_NOT_ACCEPTABLE;
			responseFail =  BaseResponse.failWithBoolean(statusCode, "请求媒体类型不允许");
		} else if (ex instanceof MissingServletRequestParameterException) {
			statusCode = HttpServletResponse.SC_BAD_REQUEST;
			responseFail =  BaseResponse.failWithBoolean(statusCode, "参数缺失");
		} else if (ex instanceof ServletRequestBindingException) {
			statusCode = HttpServletResponse.SC_BAD_REQUEST;
			responseFail =  BaseResponse.failWithBoolean(statusCode, "参数绑定失败");
		} else if (ex instanceof ConversionNotSupportedException) {
			statusCode = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
			responseFail =  BaseResponse.failWithBoolean(statusCode, "参数转换不支持");
		} else if (ex instanceof TypeMismatchException) {
			statusCode = HttpServletResponse.SC_BAD_REQUEST;
			responseFail =  BaseResponse.failWithBoolean(ResultFlagType.PARAM_TYPE_MISMATCH.getCode(), ResultFlagType.PARAM_TYPE_MISMATCH.getValue());
		} else if (ex instanceof HttpMessageNotReadableException) {
			HttpMessageNotReadableException e = (HttpMessageNotReadableException)ex;
			responseFail =  BaseResponse.failWithBoolean(ResultFlagType.HTTP_MESSAGE_NOT_READABLE.getCode(), null == e.getRootCause() ? e.getMessage() : e.getRootCause().getMessage());
		} else if (ex instanceof HttpMessageNotWritableException) {
			statusCode = HttpServletResponse.SC_BAD_REQUEST;
			responseFail = BaseResponse.failWithBoolean(statusCode, "消息内容不可写");
		} else if (ex instanceof MethodArgumentNotValidException) {
			statusCode = HttpServletResponse.SC_BAD_REQUEST;
			MethodArgumentNotValidException e = (MethodArgumentNotValidException)ex;
			BindingResult bindingResult = e.getBindingResult();
			StringBuffer sb = new StringBuffer();

			for (FieldError fieldError : bindingResult.getFieldErrors()) {
				sb.append(fieldError.getDefaultMessage()).append(SystemConsts.NEW_LINE_CHARS);
			}
			String errorMesssage = sb.toString();
			if (errorMesssage.length() > 0) {
				errorMesssage = errorMesssage.substring(0, errorMesssage.length() - SystemConsts.NEW_LINE_CHARS.length());
			}
			responseFail = BaseResponse.failWithBoolean(ResultFlagType.PARAM_INVALID.getCode(), errorMesssage);
		} else if (ex instanceof MissingServletRequestPartException) {
			statusCode = HttpServletResponse.SC_BAD_REQUEST;
			responseFail = BaseResponse.failWithBoolean(statusCode, "上传文件丢失");
		} else if (ex instanceof BindException) {
			responseFail = BaseResponse.failWithBoolean(ResultFlagType.PARAM_INVALID.getCode(), ResultFlagType.PARAM_INVALID.getValue());
		} else if (ex instanceof NoHandlerFoundException) {
			statusCode = HttpServletResponse.SC_NOT_FOUND;
			responseFail = BaseResponse.failWithBoolean(statusCode, "响应处理器匹配失败");
		} else if (ex instanceof BaseException) {
			BaseException e = (BaseException)ex;
			if(ResultFlagType.ACCESS_DENIED.getCode().toString().equals(e.getErrorCode())){
				responseFail =  BaseResponse.failWithBoolean(ResultFlagType.ACCESS_DENIED.getCode(), ResultFlagType.ACCESS_DENIED.getValue());
			} else {
				int errrCode = 999;
				try {
					errrCode = Integer.valueOf(e.getErrorCode());
				} catch(Exception ea) {
					
				}
				responseFail =  BaseResponse.failWithBoolean(errrCode, e.getErrorMsg());
			}
		} else if (ex instanceof BasePartnerException) {
			BasePartnerException e = (BasePartnerException)ex;
			responseFail =  BaseResponse.failWithBoolean(e.getErrorCode(), e.getErrorMsg());
		} else if (ex instanceof org.springframework.validation.BindException) {
			org.springframework.validation.BindException e = (org.springframework.validation.BindException)ex;
			BindingResult bindingResult =  e.getBindingResult();
			StringBuffer sb = new StringBuffer();

			for (FieldError fieldError : bindingResult.getFieldErrors()) {
				sb.append(fieldError.getDefaultMessage()).append(SystemConsts.NEW_LINE_CHARS);
			}
			String errorMesssage = sb.toString();
			if (errorMesssage.length() > 0) {
				errorMesssage = errorMesssage.substring(0, errorMesssage.length() - SystemConsts.NEW_LINE_CHARS.length());
			}
			responseFail = BaseResponse.failWithBoolean(ResultFlagType.PARAM_INVALID.getCode(), errorMesssage);
		} else if (ex instanceof MultipartException) {
			responseFail = BaseResponse.failWithBoolean(ResultFlagType.HTTP_UPLOAD_SIZE_EXCEEDED.getCode(), ResultFlagType.HTTP_UPLOAD_SIZE_EXCEEDED.getValue());
		} else if (ex instanceof QiniuException) {
			QiniuException e = (QiniuException)ex;
			responseFail = BaseResponse.failWithBoolean(e.code(), e.error());
		} else if(ex instanceof HttpClientException){
			HttpClientException e = (HttpClientException)ex;
			responseFail = BaseResponse.failWithBoolean(e.getHttpStatusCode(), e.getMessage());
		} else if (ex instanceof RuntimeException) {
			responseFail = BaseResponse.failWithBoolean(ResultFlagType.HTTP_UNEXPECTED_SERVER_ERROR.getCode(), ResultFlagType.HTTP_UNEXPECTED_SERVER_ERROR.getValue());
		} else {
			responseFail = BaseResponse.failWithBoolean(ResultFlagType.FAIL.getCode(), ResultFlagType.FAIL.getValue());
		}
		try {
			mv = new ModelAndView(new MyJsonView());
			boolean partnerSignResponseSwitch = false;
			Partners partner = PartnerHolder.getPartner();
			if(null != partner) {
				partnerSignResponseSwitch = ServerConfig.getPartnerSignResponseSwitch(partner);
			}
			if(partnerSignResponseSwitch) {
				BooleanResponse content = responseFail.getContent();
				content.setTimestamp(System.currentTimeMillis());
				ISign sign = ServerSignFactory.getSign(partner);
	        	boolean isRsaResp = false;
	     		if(sign instanceof RsaSign) {
	     			isRsaResp = true;
	     		}
				String signSecretKey = ServerConfig.getPartnerSignSecretKey(partner);
				String saltOrSecretKey = ServerSignFactory.getSignSaltOrSecretKey(partner);
				String signName = ServerConfig.getPartnerSignName(partner);
				String signCovenantKeyName = ServerConfig.getPartnerSignCovenantKeyName(partner);
				content.setSign(sign.generateSign(content, signSecretKey, saltOrSecretKey, signName, signCovenantKeyName, isRsaResp));
				String responseData = JSON.toJSONString(responseFail);
				mv.addObject(SystemConsts.ERROR_OBJECT, ServerEncryptAndDencryptUtil.encrypt(responseData, partner));
			} else {
				mv.addObject(SystemConsts.ERROR_OBJECT, responseFail);
			}
			return mv;
		} catch (Exception e) {
			logger.debug("异常处理出错了.", e);
		}
		return null;
	}
	
	/**
	 * 错误视图
	 */
	private String errorView;
	
	public String getErrorView() {
		return errorView;
	}

	public void setErrorView(String errorView) {
		this.errorView = errorView;
	}

}
